if (!Util) {
    var Util = {};
}
Util.CascadeMenu = function(_container, _option, _data, _callbacks){
    this.option = {
        display: true,
        isTraceMouse: false, // if right menu need trace mouse.
        autoHide: false, // when mouse out of menu, hide the menu
        menuContainment: "document", // "document", "window" or "Dom container"
        fixedDirection: true //  
    };
    this._menuOffset = {
        left: 0,
        top: 0
    }; // the offset to body
    this._ulSize = []; // the width for sub menu.
    this._itemDirection = []; // the show direction of sub menu.  0:left, right;     
    this.data = _data;
    this.menu;
    this.container = _container;
    this.menuContainment = [];
    this.menuItems = {};
    this.callbacks = {
        onHide: null,
        onClick: null
    }
    $.extend(this.callbacks, _callbacks);
    this._timeOut = null;
    $.extend(true, this.option, _option)
    this._init();
};
Util.CascadeMenu.currentMenu = null;
Util.CascadeMenu.prototype = {
    _init: function(isRebindData){
        this.menuItems = {};
        if (this.menu) {
            this.menu.remove();
            this.menu = null;
        }
        this.menu = this._createMenuItems(this.container, this.data, 0, "", null) || $('<ul class="rtq-menu-ul"></ul>').appendTo(this.container);// create the cascade menu UI.
        this.menu.addClass("rtq-menu").hide();
        var self = this;
        this._itemBindEvent(this.menu, this);
        this.menu.unbind("click.cascadeMenu").bind("click.cascadeMenu", this, this.menuOnClick).bind("mouseover.cascadeMenu", function(e){
            self.show();
            if (self._timeOut) {
                clearTimeout(self._timeOut)
            }
        });
        if (this.option.autoHide) {
            this.menu.unbind("mouseout.cascadeMenu").bind("mouseout.cascadeMenu", function(e){
                if (self._timeOut) {
                    clearTimeout(self._timeOut)
                }
                self._timeOut = setTimeout(function(){
                    self.hide();
                }, 300);
            });
        }
        
        $(window).resize(function(){
            self._setDragContainment();
        });
        
        if (!this.option.display && !isRebindData) {
            this.hide();
        }
        else {
            this._menuOffset = this.menu.offset();
        }
        this._setDragContainment();
    },
    _itemBindEvent: function(obj, self){
        var sf = this;
        obj.find(".rtq-menu-item").bind("mouseenter", this, function(e){
            var sf = e.data;
            var el = $(this); // bind the hover event to the menu item.
            el.addClass("rtq-menu-hover"); // right direction.
            var idx = el.attr("index");
            var tip = sf.menuItems[idx].data.tip;
            if (typeof(tip) != "undefined") {
                var tipEl = sf.container.find(".cascademenu-tip");
                if (tipEl.length > 0) {
                    tipEl.show();
                }
                else {
                    sf.container.append("<div class='cascademenu-tip'>" + tip + "</div>");
                    tipEl = sf.container.find(".cascademenu-tip");
                    tipEl.show();
                }
                tipEl.html(tip);
                tipEl.css({
                    top: el.position().top + 23,
                    left: el.parent().position().left + el.width()
                });
            }
            if (!self.option.fixedDirection) { // need to set direction
                var layer = el.parent().data("layer.CascadeMenu");
                var ulEl = el.find(">.rtq-menu-ul");
                if (ulEl.length) { // has sub menu.
                    self._ulSize[layer + 1] = {
                        width: ulEl.width(),
                        height: ulEl.height()
                    };
                    var d = self._setDirection(layer + 1, el.offset().top), h, v;
                    if (d.horizontal) {
                        h = "100%";
                    }
                    else {
                        h = (-self._ulSize[layer + 1].width);
                    }
                    if (d.vertical) {
                        v = 0;
                    }
                    else {
                        v = (-self._ulSize[layer + 1].height + 15); // down 20 px.
                    }
                    if (d.drag) {
                        v = v + d.drag;
                    }
                    ulEl.css({
                        left: h,
                        top: v
                    });
                }
            }
            else {
                var ulEl = el.find(">.rtq-menu-ul");
                if (ulEl.css("top") != 0 || ulEl.css("top") != '0px') {
                    ulEl.css("top", '0px')
                }
                if (ulEl.size() == 0) {
                    var ulEl = el;
                }
                var offsetTop = ulEl.offset().top;
                var height = ulEl.height();
                var limit = sf.menuContainment[1] + sf.menuContainment[3];
                if (offsetTop + height > limit) {
                    if (height > limit) {
                        ulEl.css({
                            top: -offsetTop
                        });
                    }
                    else {
                        ulEl.css({
                            top: limit - (offsetTop + height)
                        });
                    }
                }
            }
        }).bind("mouseleave", this, function(e){
            $(this).removeClass("rtq-menu-hover");
            if (typeof(e.data.container.find) == "function") {
                e.data.container.find(".cascademenu-tip").hide();
            }
        });
    },
    _getDataObj: function(sq, index){// 12345...
        if (typeof(sq) == "undefined" || sq == null) 
            return null;
        var data = null, indx;
        for (var i = 0; i <= sq; i++) {
            indx = parseInt(index[i]);
            if (i == 0) 
                data = this.data[indx];
            else 
                data = data.subMenu[indx];
        }
        return data;
    },
    reBindSecondLevelMenu: function(val, data, sq){
        var item = this.getItemByVal(val);
        if(!item.el.hasClass("rtq-menu-arrow")){
            item.el.addClass("rtq-menu-arrow")
        }
        item.el.find(".rtq-menu-ul").remove();
        this._createMenuItems(item.el, data, sq, item.el.attr("index"), item);
        this._itemBindEvent(item.el, this);
    },
    _createMenuItems: function(container, data, sq, inds, parent){ // sq is series index.
        var str = "", cls = "", cls2 = "", ind = inds, ty, $ul, preCls = "";
        if (data == null) 
            return;
        if (data.length) {
            $ul = $('<ul class ="rtq-menu-ul"></ul>').appendTo(container).data("layer.CascadeMenu", sq);
            for (var i = 0; i < data.length; i++) {
                if (!data[i]) {
                    continue;
                }
                if (data[i].display == false) {
                    continue;
                }
                preCls = typeof(data[i]["className"]) != "undefined" ? data[i]["className"] : ""; // custome class.
                ind = inds + "_" + i;
                this.menuItems[ind] = {};
                this.menuItems[ind]["data"] = this._copyItemData(data[i]);
                this.menuItems[ind]["parent"] = parent;
                this.menuItems[ind]["el"] = $('<li index=' + ind + ' class="rtq-menu-item ' + preCls + ((data[i].subMenu && data[i].subMenu.length) ? ' rtq-menu-arrow' : '') + (data[i].disable ? ' disable' : '') + '"></li>').appendTo($ul);
                if (typeof(data[i].renderer) == "function") {
                    data[i].renderer(this.menuItems[ind]["el"]);
                }
                else 
                    if (data[i].type == "separator") {
                        this.menuItems[ind]["el"].append('<div class="rtq-menu-separator"></div>');
                    }
                    else {
                        cls2 = "rtq-menu-a";
                        if (data[i].type == "checkbox") {
                            cls = 'class = "' + (data[i].checked ? 'rtq-chkbox ' : 'rtq-chkbox rtq-chkbox-off ') + cls2 + '"';
                        }
                        else if (data[i].type == "ratio") {
                            cls = 'class = "rtq-ratio rtq-ratio-off ' + cls2 + '"';
                        }else if (data[i].type == "switch") {
                            if (data[i].txt == data[i].sTxt[0]) {
                                cls = 'class = "on ' + cls2 + '"';
                            }
                            else {
                                cls = 'class = "' + cls2 + '"';
                            }
                        }else {
                            cls = 'class = "' + cls2 + '"';
                        }
                        ty = data[i].type ? data[i].type : "&nbsp;";
                        str = '<a ' + cls + 'type=' + ty + ' sq=' + sq + ' >' + data[i].txt + '</a>';
                        this.menuItems[ind]["el"].append(str);
                    }
                if (data[i].subMenu && data[i].subMenu.length) {
                    this._createMenuItems(this.menuItems[ind]["el"], data[i].subMenu, sq + 1, ind, this.menuItems[ind]);
                }
                if (typeof(data[i].selected) != "undefined" && data[i].selected == true){
                    this.menuItems[ind]["el"].children().removeClass("rtq-ratio-off");
                }
                if (data[i].disable === true) {
                    this.menuItems[ind]._isDisable = true;
                }
            }
        }
        return $ul;
    },
    _copyItemData: function(data){
        var d = {};
        for (var o in data) {
            if (o != "subMenu") {
                d[o] = data[o];
            }
        }
        return d;
    },
    _getPostion: function(e, $container){
        var scrollPosition=Util.getScrollPosition();
        var oSet = $container.offset();
        return {
            left: e.pageX + scrollPosition.scrollx - oSet.left,
            top: e.pageY + scrollPosition.scrolly - oSet.top
        };
    },
    setPosition: function(_left, _top){
        this.menu.css({
            top: _top,
            left: _left
        });
    },
    _selectMenuItem: function(item, flag){
        var ty = item.data.type, t = item.el.find(">a"), hl = item.data.autoHighlight;
        /*if (typeof(hl) != "undefined" && hl) {
            if (t.hasClass("rtq-ratio-off")) { /// not selcted
                t.removeClass("rtq-ratio-off");
            }else if (!item.el.hasClass("rtq-menu-on")) {
                item.el.addClass("rtq-menu-on");
            }
        }*/
        if (ty == "ratio") {
            if (!flag) {  // uncheck
                t.addClass("rtq-ratio-off");
            }else {  // check
                //t.closest("ul", this.menu).find(">li a").addClass("rtq-ratio-off");  // uncheck all the 
                if (t.hasClass("rtq-ratio-off")) { /// not selcted
                    t.closest("ul", this.menu).find(">li>a").addClass("rtq-ratio-off");
                    t.removeClass("rtq-ratio-off");
                } else if (item.el.hasClass("rtq-menu-on")) {
                    t.closest("ul", this.menu).find(">li").removeClass("rtq-menu-on");
                }
            }
        }else if (ty == "checkbox") {
            if (flag) {
                t.removeClass("rtq-chkbox-off");
            }else {
                t.addClass("rtq-chkbox-off");
            }
            item.checked = flag;
        }else if (ty == "switch") {
            if (flag) {
                t.addClass("on");
            }else {
                t.removeClass("on");
            }
            var inx = flag ? 0 : 1;
            t.html(item.data.sTxt[inx]);
            item["switch"] = flag ? "on" : "off";
        }
    },
    menuOnClick: function(e){
        var self = e.data;
        var flag = true;
        var t = $(e.target).closest(".rtq-menu-item", self.menu);
        var index = t.attr("index");
        if (typeof(index) == "undefined") 
            return false;
        var item = self.menuItems[index];
        if (item._isDisable === true) 
            return false;
        if (item.data && (typeof(item.data.selectable) == "undefined" || item.data.selectable)) { // if not define selectable its default value is true.
            if (item.data.type == "checkbox") {
                flag = t.find("a").hasClass("rtq-chkbox-off"); // not checked.
            }
            else 
                if (item.data.type == "switch") {
                    flag = !t.find("a").hasClass("on");
                }
            self._selectMenuItem(item, flag);
            if (typeof(self.callbacks.onClick == "function")) {
                self.callbacks.onClick(item);
            }
        }
    },
    show: function(e){
        if (Util.CascadeMenu.currentMenu != null && Util.CascadeMenu.currentMenu != this) {
            Util.CascadeMenu.currentMenu.hide();
        }
        var marg = 0, pos;
        if (typeof(this._ulSize[0]) == "undefined") 
            this._ulSize[0] = {
                width: this.menu.width(),
                height: this.menu.height()
            };
        if (this.option.isTraceMouse && typeof(e) != "undefined") {
            this._menuOffset = {
                left: e.pageX,
                top: e.pageY
            };
            pos = this._getPostion(e, this.container);
            this._setDragContainment();
            var direction = this._setDirection(0, pos.top);//this._itemDirection[layer] = {horizontal:h,vertical:v};
            if (!direction.horizontal) {// left
                pos.left = pos.left - this._ulSize[0].width
            }
            if (!direction.vertical) {
                pos.top = pos.top - this._ulSize[0].height;
            }
            if (direction.drag) {
                pos.top = pos.top + direction.drag;
            }
            this.menu.css(pos).show();
            this._itemDirection[0] = {
                horizontal: 1,
                vertical: 1
            }; // left and down.
            this._menuOffset = this.menu.offset();
        }
        else {
            this.menu.show();
        }
        Util.CascadeMenu.currentMenu = this;
        clearTimeout(this._timeOut); //david
    },
    hide: function(){
        this.menu.hide();
        if (this.callbacks.onHide) {
            this.callbacks.onHide();
        }
    },
    getDisplayState: function(){
        return this.menu.css("display") != "none";
    },
    getItemByVal: function(val, parentVal){
        var d, p;
        for (var o in this.menuItems) {
            d = this.menuItems[o].data;
            p = this.menuItems[o].parent == null ? null : this.menuItems[o].parent.data;
            if (val == d.val && (typeof(parentVal) == "undefined" || parentVal == null || (p != null && parentVal == p.val))) { // find the menuItem
                return this.menuItems[o];
            }
        }
        return null;
    },
    changeUIState: function(val, parentVal, flag){ // only change the UI state not excute callback.
        var item = this.getItemByVal(val, parentVal);
        if (item) {
            this._selectMenuItem(item, flag);
        }
    },
    disableItem: function(val, parentVal){
        var item = this.getItemByVal(val, parentVal);
        if (item&&!item._isDisable) {
            item._isDisable = true;
            item.el.addClass('disable');
        }
    },
    enableItem: function(val, parentVal){
        var item = this.getItemByVal(val, parentVal);
        if (item&&item._isDisable) {
            item._isDisable = false;
            item.el.removeClass('disable');
        }
    },
    changeItemClass: function(val, parentVal, className, flag, isChild){ // remove or add a class name to menu item.
        var d, p;
        if (typeof(isChild) == "undefined") {
            isChild = false;
        }
        for (var o in this.menuItems) {
            d = this.menuItems[o].data;
            p = this.menuItems[o].parent == null ? null : this.menuItems[o].parent.data;
            if (val == d.val && (typeof(parentVal) == "undefined" || parentVal == null || (p != null && parentVal == p.val))) { // find the menuItem
                if (isChild) {
                    if (flag) {
                        this.menuItems[o].el.find(">a").addClass(className)
                    }
                    else {
                        this.menuItems[o].el.parent().find("li a").addClass(className);
                        this.menuItems[o].el.find(">a").removeClass(className);
                    }
                }
                else {
                    flag ? this.menuItems[o].el.addClass(className) : this.menuItems[o].el.removeClass(className);
                }
                
            }
        }
    },
    reBind: function(data, flag){
        this.data = data;
        this._init(flag);
    },
    _setDragContainment: function(){ // set the menu containment
        var o = this.option;
        if (o.menuContainment == 'document' || o.menuContainment == 'window') {
            this.menuContainment = [0, 0, $(o.menuContainment == 'document' ? document : window).width(), $(document.body.parentNode).height()];
        }
        if (!(/^(document|window)$/).test(o.menuContainment) && o.menuContainment.constructor != Array) { // Dom element
            var ce = $(o.menuContainment)[0];
            if (!ce) 
                return;
            var co = $(o.menuContainment).offset();
            var over = ($(ce).css("overflow") != 'hidden');
            
            this.menuContainment = [(parseInt($(ce).css("borderLeftWidth"), 10) || 0) + (parseInt($(ce).css("paddingLeft"), 10) || 0), (parseInt($(ce).css("borderTopWidth"), 10) || 0) + (parseInt($(ce).css("paddingTop"), 10) || 0), (over ? Math.max(ce.scrollWidth, ce.offsetWidth) : ce.offsetWidth) - (parseInt($(ce).css("borderLeftWidth"), 10) || 0) - (parseInt($(ce).css("paddingRight"), 10) || 0), (over ? Math.max(ce.scrollHeight, ce.offsetHeight) : ce.offsetHeight) - (parseInt($(ce).css("borderTopWidth"), 10) || 0) - (parseInt($(ce).css("paddingBottom"), 10) || 0)];
        }
        else 
            if (o.menuContainment.constructor == Array) {
                this.menuContainment = o.menuContainment;
            }
        
    },
    _setDirection: function(layer, offsetTop){
        if (layer < 0 || typeof(layer) == "undefined" || this._itemDirection.length < layer) 
            return 1;
        var preW = 0, preH = 0, h, v, drag = 0;
        for (var i = 0; i < layer; i++) {
            if (this._itemDirection[i].horizontal == 1) 
                preW += this._ulSize[i].width;
            else 
                preW -= this._ulSize[i].width;
            if (this._itemDirection[i].vertical == 1) 
                preH += this._ulSize[i].height;
            else 
                preH -= this._ulSize[i].height;
        }
        if (this._menuOffset.left + preW + this._ulSize[layer].width > (this.menuContainment[0] + this.menuContainment[2])) {
            h = 0; // left;
        }
        else {
            h = 1; //right
        }
        if (offsetTop + this._ulSize[layer].height > (this.menuContainment[1] + this.menuContainment[3])) {
            v = 0;//up
            if (offsetTop < this._ulSize[layer].height - (offsetTop == 0 ? 0 : 15) &&
            (this.menuContainment[1] + this.menuContainment[3]) > this._ulSize[layer].height) {// menu is higher the offsetTop
                v = 1;//down
                drag = (this.menuContainment[1] + this.menuContainment[3]) - (offsetTop + this._ulSize[layer].height);
            }
            else 
                if (offsetTop < this._ulSize[layer].height - (offsetTop == 0 ? 0 : 15)) {
                    drag = this._ulSize[layer].height - (offsetTop);
                }
        }
        else {
            v = 1; //down
        }
        this._itemDirection[layer] = {
            horizontal: h,
            vertical: v,
            drag: drag
        };
        return this._itemDirection[layer];
    },
    clear: function(){
        this.menu.unbind(".cascadeMenu");
        this.menu.find(".rtq-menu-item").unbind();
        for (var o in this.menuItems) {
            this.menuItems[o].parent = null;
            this.menuItems[o].el = null;
            this.menuItems[o].data = null;
        }
        this.data = null;
        this.menuItems = null;
        this.menu.remove();
    }
};